#ifndef A_NUMERIC_H_
#define A_NUMERIC_H_

/*!
	\page th_numeric Standard numeric algorithms
	
	STL's file \<numeric\> provides four simple algorithms operating with
	ranges. They are called \em numeric because they usually make a deal with
	containers of arithmetic values (i.e. values of types, which supports
	operations of addition and subtraction), but its application field is
	wider then just an arithmetic.
	
	Well, standard numeric algorithms are:
	\li \c accumulate, which main purpose is an \em{incremenatal accumulation}
		of information about all elements of a specified range.
	\li \c inner_product, which technically can be described as an analog
		of \c accumulate with two inputs.
	\li \c partial_sum and \c adjacent_difference.
	
	All of these functions receive one or two ranges as a pair of iterators.
	Alib provides wrappers for them, which can receive the whole container
	or any other range object (see \ref th_range for more information).
	Accordingly to the C++ Standard, numeric operation shall neither modify
	input elements nor invalidate iterators or subranges. This requirement
	forces us to use only constant range. If you need an operation which
	can modify its input (maybe for some hacking purposes), please use the
	native STL version of these functions.
	
	\section th_numeric_accum Accumulation
	
	The main purpose of the function is an \em{incremental accumulation} of
	information about all elements of a range. That means the algorithm
	sequentally iterates over the whole range, extract some information
	about each element and adds it to the accumulator. 
	
	Simplest application of accumulate function is summarize of range
	entries. For example, if \a c is a container of three integers (e.g. 2, 3
	and 7) then the code
	\code
		std::accumulate(c.begin(), c.end(), 0);
	\endcode
	will result 2 + 3 + 7 = 12.
	
	Note the result type of the function and type of the container's content
	can not be the same. In fact, it controls by the third parameter \a init.
	Its meanings is an initial value of the accumulator, and the function
	returns the final value of the accumulator.
	
	So, you can force the function to return \c float:
	\code
		std::accumulate(c.begin(), c.end(), 0.f);
	\endcode
	will result floating-point value 12.0, because each iteration will add
	integer value to the floating-point accumulator.
	
	Now consider the following example:
	\code
		char * hello  = "Hello world!";
		char * result = std::accumulate(c.begin(), c.end(), hello);
	\endcode
	What value the \a result will have? Obviously, it will \a hello + 2 + 3 + 7,
	or \c{&hello[12]}, which is an address of the string's terminating zero.
	So, acumulate function is useful for advance some random-access iterator
	or usual pointers.
	
	Also the \c accumulate function works correctly even if addition of
	container's elements is not defined or meaningless in a context of your
	task. For instance, you cannot add two pointers, but you can 'add'
	pointer-to-\c char to some \c{std::string}:
	\code
		std::string hello    = "Hello ";
		char *      world[3] = { "wo", "rl", "d!" };
		std::string result   = std::accumulate(world, world + 3, hello);
	\endcode
	From the other hand, when \c{char} variables are used for storing letters,
	its addition is avalible but has no representation as letter operation.
	But \c{std::string + char} is legal operation of string concatenation:
	\code
		std::string hello    = "Hello ";
		char        world[6] = { 'w', 'o', 'r', 'l', 'd', '!' };
		std::string result   = std::accumulate(world, world + 6, hello);
	\endcode
	The \a result in the last two examples is \c{std::string("Hello world!")}.
	
	You also can specify an \em{accumulation function} passing 
*/


/*
	Type of the \a range formal argument is not \c{range_type const &} because
	in some pathological cases operator + can modify its input, i.e. items
	in the range. From the other hand having two overloaded versions for both
	constant and non-constant \a range is not necessary, because the function
	for constant range would be called only when the fact argument \a range
	is a constant or reference-to-constant.
*/


#include <numeric>


/*

*/
  
//! Computes the sum of a specified range.
/*!
	The function executes the code \c{res = res + *it} for every iterator \a it
	in the \a range. In that code \a res is accumulating variable of type
	\a result_type; at the beginning its value is \a init. Final value of
	\a res is treated as the result of function.
	
	\param range Range, sequence or container to summarize.
	\param init  Initial value of the accumulator.
	
	\return The function's result is \f$ r \f$, which computes by the
	following formula:
	\f[
		r = (((r_0 + a_1) + a_2) + \ldots + a_n)
	\f]
	where \f$ r_0 \f$ is \a init, \f$ a_1, \ldots, a_n \f$ are elements of
	an input \a range of size \f$ n \f$. So that, if \a range is empty, the
	function will return \a init.
*/
template
<
    class range_type,
    class result_type
>
result_type accumulate
(
    range_type const & range,
    result_type        init
)
{
    return std::accumulate(range.begin(), range.end(), init);
}


//! Accumulates information about all elements of a range.
/*!
	The function executes the code \c{res = accum(res\, *it)} for every iterator
	\a it in the \a range. In that code \a res is accumulating variable of type
	\a result_type; at the beginning its value is \a init. Final value of
	\a res is treated as the result of function.
	
	\param range Range, sequence or container to summarize.
	\param init  Initial value of the accumulator.
	\param accum Accumulating function or functional object.
	
	\note Accumulation function must have one of the following signatures:
	\code
		result_type accum( result_type res, range_type::reference_type val );
		result_type accum( result_type res, range_type::value_type val );
	\endcode
	It gets accumulating variable \a res and one of the \a range's elements
	\a val, adds some information about \a val to the accumulator and returns
	accumulator's new value. For example, if you want to compute the total
	length of text strings storing in some container, you can use a functional
	object of the following class:
	\code
		class total_length_accum
		{
		public:
			size_t operator()( size_t res, std::string const & str )
			{
				return res + str.length();
			}
		};
	\endcode
	
	\return The function's result is \f$ r_n \f$, which computes by the
	following formula:
	\f[
		\def\accum{\operatorname{accum}}
		r_k = \accum(r_{k - 1}, a_k),
		\qquad \text{where } k = 1, \ldots, n
	\f]
	where \f$ r_0 \f$ is \a init, \f$ a_1, \ldots, a_n \f$ are elements of
	an input \a range of size \f$ n \f$. So, if \a range is empty, the
	function will return \a init.
	
	\note Using an external object as an accumulator can be brilliant idea.
	To make it, pass a pointer to your accumulator as \a init value, and 
	
*/
template
<
    class range_type,
    class result_type,
    class accum_func_type
>
result_type accumulate
(
    range_type const & range,
    result_type        init,
    accum_func_type    accum
)
{
    return std::accumulate(range.begin(), range.end(), init, accum);
}


//! Computes the sum of element-wise products of two ranges.
/** This functions requires the size of \a range2 is not less then the size of
  * \a range1. Provided that, the function does not test ranges for this
  * subject.
  */
template
<
    class range1_type,
    class range2_type,
    class result_type
>
result_type inner_product
(
    range1_type const & range1,
    range2_type const & range2,
    result_type         init
)
{
    return std::inner_product
        (range1.begin(), range1.end(), range2.begin(), init);
}


/** The function combines elements of two ranges with \a combine function and
  * summarizes the result using \a accum function. */
template
<
    class range1_type,
    class range2_type,
    class result_type,
    class accum_func_type,
    class combine_func_type
>
result_type inner_product
(
    range1_type const & range1,
    range2_type const & range2,
    result_type         init,
    accum_func_type     accum,
    combine_func_type   combine
)
{
    return std::inner_product
        (range1.begin(), range1.end(), range2.begin(), init, accum, combine);
}


/** \todo A version of partial_sum with initial value */
template
<
    class range_type,
    class output_iter_type
>
output_iter_type partial_sum
(
    range_type const & range,
    output_iter_type   it_out
)
{
    return std::partial_sum(range.begin(), range.end(), it_out);
}


template
<
    class range_type,
    class output_iter_type,
    class accum_func_type
>
output_iter_type partial_sum
(
    range_type const & range,
    output_iter_type   it_out,
    accum_func_type    accum
)
{
    return std::partial_sum(range.begin(), range.end(), it_out, accum);
}


/** \todo A version of adjacent_difference with initial value.
  * \todo A version of adjacent_difference skipping first entry. */
template
<
    class range_type,
    class output_iter_type
>
output_iter_type adjacent_difference
(
    range_type const & range,
    output_iter_type   it_out
)
{
    return std::adjacent_difference(range.begin(), range.end(), it_out);
}


template
<
    class range_type,
    class output_iter_type
    class diff_func_type
>
output_iter_type adjacent_difference
(
    range_type const & range,
    output_iter_type   it_out,
    diff_func_type     diff
)
{
    return std::adjacent_difference(range.begin(), range.end(), it_out, diff);
}


#endif /* A_NUMERIC_H_ */
